package cn.seaboot.admin.excel.manager;

import cn.seaboot.admin.excel.bean.ExcelLog;
import cn.seaboot.admin.excel.bean.ExcelLogItem;
import cn.seaboot.admin.excel.bean.ExcelRow;
import cn.seaboot.admin.excel.bean.ExcelSource;
import cn.seaboot.admin.excel.service.ExcelLogItemService;
import cn.seaboot.admin.excel.service.ExcelLogService;
import cn.seaboot.admin.excel.service.ExcelRowService;
import cn.seaboot.admin.excel.service.ExcelSourceService;
import cn.seaboot.admin.user.bean.User;
import cn.seaboot.admin.user.context.UserContext;
import cn.seaboot.commons.core.FastJsonUtils;
import cn.seaboot.commons.core.Snowflake;
import cn.seaboot.commons.core.SnowflakeFactory;
import cn.seaboot.commons.file.MediaType;
import cn.seaboot.commons.file.ResponseWriter;
import cn.seaboot.excel.ExcelBook;
import cn.seaboot.excel.call.ExcelCallback;
import cn.seaboot.excel.call.HtmlConsumer;
import cn.seaboot.excel.call.RowReadCallback;
import org.apache.poi.ss.usermodel.Row;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.function.Consumer;

/**
 * Excel 通用解析程序
 *
 * @author Mr.css
 * @version 2025-04-18 11:25
 */
@Service
public class ExcelManager {
    private final Snowflake snowflake = SnowflakeFactory.createSnowflake();
    private final Logger logger = LoggerFactory.getLogger(ExcelManager.class);

    @Resource
    private UserContext userContext;

    @Resource
    private ExcelRowService excelRowService;

    @Resource
    private ExcelSourceService excelSourceService;

    @Resource
    private ExcelLogService excelLogService;

    @Resource
    private ExcelLogItemService excelLogItemService;


    /**
     * 将 Excel 转为 html 脚本，供前端进行内容查看
     */
    public void downloadBook(HttpServletResponse response, Long id) throws IOException {
        ExcelSource excel = excelSourceService.queryById(id);
        if (excel == null) {
            response.getWriter().write("File not found!");
        } else {
            try (InputStream is = excelSourceService.openInputStream(excel.getExcelPath())) {
                ResponseWriter.download(response, is, excel.getExcelName());
            }
        }
    }

    /**
     * 将 Excel 转为 html 脚本，供前端进行内容查看
     */
    public void previewBook(HttpServletResponse response, Long id) throws IOException {
        ExcelSource source = excelSourceService.queryById(id);
        if (source == null) {
            response.getWriter().write("File not found!");
        } else {
            response.setContentType(MediaType.TEXT_HTML_VALUE);
            response.setCharacterEncoding("UTF-8");
            Writer writer = response.getWriter();
            writer.write("<table><tbody>\n");
            Consumer<Row> consumer = new HtmlConsumer(writer);
            try (InputStream is = excelSourceService.openInputStream(source.getExcelPath());
                 ExcelBook book = ExcelBook.create(is)) {
                book.sheetAt(0).setStartRow(0).eachRow(consumer);
            }
            writer.write("</tbody></table>\n");
        }
    }


    /**
     * 将 Excel 内容存入数据库
     *
     * @param source excel的存储信息
     */
    public void writeBook(ExcelSource source) throws IOException {
        RowReadCallback<Object[]> reader = ExcelCallback.objectArrayCallback();
        try (InputStream is = excelSourceService.openInputStream(source.getExcelPath());
             ExcelBook book = ExcelBook.create(is)) {
            int cnt = 0;
            for (Row row : book.sheetAt(0).setStartRow(0)) {
                Object[] arr = null;
                if (row != null) {
                    arr = reader.readRow(row);
                }
                writeContent(source.getId(), cnt, arr);
                cnt++;
            }
        }
    }

    /**
     * 保存行级数据
     *
     * @param id      文件 ID
     * @param rowNum  行号
     * @param content 内容
     */
    public void writeContent(Long id, Integer rowNum, Object[] content) {
        String str = content != null ? FastJsonUtils.toJSONString(content) : null;

        ExcelRow pojo = new ExcelRow();
        pojo.setId(snowflake.nextId());
        pojo.setSourceId(id);

        pojo.setRowNo(rowNum);
        pojo.setContent(str);
        excelRowService.insert(pojo);
    }

    /**
     * 载入 Excel
     * <p>
     * 未完成，基本结构如此
     *
     * @param source excel的存储信息
     */
    public <T> void loadBook(ExcelSource source, RowReadCallback<T> reader, Consumer<T> consumer) throws IOException {
        // 保存日志
        ExcelLog log = new ExcelLog();
        log.setId(snowflake.nextId());

        User user = userContext.getUser();
        log.setUserId(user.getId());
        log.setUserName(user.getUsername());

        log.setSourceId(source.getId());
        excelLogService.insert(log);

        // 逐行处理数据
        try (InputStream is = excelSourceService.openInputStream(source.getExcelPath());
             ExcelBook book = ExcelBook.create(is)) {
            for (Row row : book.sheetAt(0).setStartRow(0)) {
                if (row != null) {
                    // 逐行处理数据
                    this.loadRow(row, log.getId(), reader, consumer);
                }
            }
            this.onLoadSucceed(log);
        } catch (Exception e) {
            this.onLoadFailed(log, e);
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * 载入失败
     *
     * @param log 日志
     * @param e   异常
     */
    private void onLoadFailed(ExcelLog log, Exception e) {
    }

    /**
     * 载入成功
     *
     * @param log 日志
     */
    private void onLoadSucceed(ExcelLog log) {
    }

    /**
     * 载入 Excel
     */
    public <T> void loadRow(Row row, Long batchId, RowReadCallback<T> reader, Consumer<T> consumer) {
        // 通用数据读取工具
        T t = reader.readRow(row);

        // 进一步完成转换逻辑
        consumer.accept(t);

        // 保存行级日志
        ExcelLogItem item = new ExcelLogItem();
        item.setBatchId(batchId);
        item.setId(snowflake.nextId());
        excelLogItemService.insert(item);
    }
}
